using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using System.Runtime.Remoting.Messaging;

using usm.SolDev.DataBase.DataObjects;
using usm.SolDev.DataBase.DataAccess;
using usm.SolDev.Common.Constants;
using usm.SolDev.Common;

namespace usm.SolDev.UI.User
{
    public partial class UserManagement : usm.SolDev.UI.BaseForm
    {
        #region Column names
        const string COLUMN_ID = "id";
        const string COLUMN_NAME = "name";
        const string COLUMN_GENERIC = "generic";
        #endregion

        #region Context menu 
        enum ContextMenuParrent { cmpUsers, cmpGroups };
        ContextMenuParrent contextMenuParent;
        #endregion

        #region Constructors
        /// <summary>
        ///     Default constructor
        /// </summary>
        public UserManagement()
        {
            InitializeComponent();
        }
        #endregion

        #region Form events handlers
        /// <summary>
        ///     Load data from database. Users, groups and rights.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void UserManagement_Load(object sender, EventArgs e)
        {
            (new GetUserManagementDataDelegate(DataProvider.DataBase.GetUserManagementData)).BeginInvoke(WorksessionData.UserInfo, new AsyncCallback(GetUserManagementDataCallBack), null);
            base.StartWaiting ();
        }

        /// <summary>
        ///     Prevent form closing if there are still data which has not bee saved.
        /// </summary>
        /// <param name="sender">the event sender</param>
        /// <param name="e">the form closing events (cancel closing trough them)</param>
        private void UserManagement_FormClosing(object sender, FormClosingEventArgs e)
        {
            if (areObjectStatesModified() == true && (e.CloseReason != CloseReason.WindowsShutDown))
            {
                if (MessageBox.Show("There are unsaved data, discard them?", "Save data ... ", MessageBoxButtons.YesNo) != DialogResult.Yes)
                {
                    e.Cancel = true;
                }
            }
        }

        /// <summary>
        ///     On item changed (active user is changed) , update the related information. The 
        /// privileged info and the 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void lbUsers_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (lbUsers.SelectedItem != null)
            {
                UserInfoData userInfo = (UserInfoData)((DataRowView)lbUsers.SelectedItem).Row[COLUMN_GENERIC];

                #region Update group information
                for ( int i = 0; i < clbGroups.Items.Count; i ++)
                {
                    clbGroups.SetItemChecked(i, ((GroupDataObject)clbGroups.Items[i]).Id == userInfo.Group);
                }
                #endregion

                #region Update group information
                for (int i = 0; i < clbAccessRights.Items.Count; i++)
                {
                    clbAccessRights.SetItemChecked(i, userInfo.HasPermission(((Permissions)clbAccessRights.Items[i]).Id));
                }
                #endregion
                
            }
        }

        /// <summary>
        ///     Change the group of the selected user
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        /// <summary>
        /// 
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        bool groupIsChanging;
        private void clbGroups_ItemCheck(object sender, ItemCheckEventArgs e)
        {
            if (lbUsers.SelectedItem == null || groupIsChanging == true)
            {
                return;
            }

            groupIsChanging = true;
            UserInfoData userInfo = (UserInfoData)((DataRowView)lbUsers.SelectedItem).Row[COLUMN_GENERIC];

            #region Unckek other items
            for (int i = 0; i < clbGroups.Items.Count; i++)
            {
                if (clbGroups.SelectedIndex != i)
                {
                    clbGroups.SetItemChecked(i, false);
                }
            }
            #endregion

            #region set user group
            if (clbGroups.SelectedIndex >= 0)
            {
                userInfo.Group = 0;
                userInfo.State = usm.SolDev.Common.Config.ObjectState.Modified;

                if (clbGroups.GetItemChecked(clbGroups.SelectedIndex))
                {
                    userInfo.Group = ((GroupDataObject)clbGroups.SelectedItem).Id;
                }
            }
            #endregion

            groupIsChanging = false;
        }

        /// <summary>
        ///     On menu invoked, keep the invocation source
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void cmMenu_Opened(object sender, EventArgs e)
        {
            if (cmMenu.SourceControl == null)
            {
                return;
            }

            if (cmMenu.SourceControl.Parent.Name.Equals("gbGroups"))
            {
                contextMenuParent = ContextMenuParrent.cmpGroups;
            }

            if (cmMenu.SourceControl.Parent.Name.Equals("gbUsers"))
            {
                contextMenuParent = ContextMenuParrent.cmpUsers;
            }
        }

        /// <summary>
        ///     Perform menu selected action
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void MenuItemClick(object sender, EventArgs e)
        {
            #region Add new user or group
            if (sender == menuItemAddNew)
            {
                if (contextMenuParent == ContextMenuParrent.cmpGroups)
                {
                    using(CreateGroup cg = new CreateGroup())
                    {
                        if (cg.ShowDialog() == DialogResult.OK)
                        {
                            GroupDataObject groupData = cg.Group;
                            clbGroups.Items.Add(groupData);
                            groupData.Id = clbGroups.Items.Count;
                        }

                    }
                }
                else if (contextMenuParent == ContextMenuParrent.cmpUsers)
                {
                    UserInfoData newUserInfoData = new UserInfoData();
                    newUserInfoData.State = usm.SolDev.Common.Config.ObjectState.New;
                    List<GroupDataObject> groups = new List<GroupDataObject>();

                    foreach(GroupDataObject groupDataObject in  clbGroups.Items)
                    {
                        groups.Add(groupDataObject);
                    }

                    using (CreateUser cu = new CreateUser(newUserInfoData, groups))
                    {
                        if (cu.ShowDialog() == DialogResult.OK)
                        {
                            ((DataTable)(lbUsers.DataSource)).Rows.Add(new object[] { cu.UserInfoData.Id, cu.UserInfoData.Name, cu.UserInfoData });
                            lbUsers.DisplayMember = COLUMN_NAME;
                        }
                    }
                }
            }
            #endregion
            
            #region Delete users & groups
            else  if (sender == menuItemDelete)
            {
                if (contextMenuParent == ContextMenuParrent.cmpGroups && clbGroups.SelectedItem != null)
                {
                    if (((GroupDataObject)clbGroups.SelectedItem).State == usm.SolDev.Common.Config.ObjectState.New)
                    {
                        clbGroups.Items.RemoveAt(clbGroups.SelectedIndex);
                    }
                    else
                    {
                        // call remove delegate from DB
                    }
                }
                else if (contextMenuParent == ContextMenuParrent.cmpUsers && lbUsers.SelectedItem != null)
                {
                    if(((UserInfoData)((DataTable)(lbUsers.DataSource)).Rows[lbUsers.SelectedIndex][COLUMN_GENERIC]).State == usm.SolDev.Common.Config.ObjectState.New)
                    {
                        ((DataTable)(lbUsers.DataSource)).Rows.RemoveAt(lbUsers.SelectedIndex);
                    }
                }
            }
            #endregion
            
            #region Edit Users & Groups
            else  if (sender == menuItemEdit)
            {
                if (contextMenuParent == ContextMenuParrent.cmpGroups && clbGroups.SelectedItem != null)
                {
                    ((GroupDataObject)clbGroups.SelectedItem).State = usm.SolDev.Common.Config.ObjectState.Modified;
                    using (CreateGroup cg = new CreateGroup((GroupDataObject)clbGroups.SelectedItem))
                    {
                        if (cg.ShowDialog() == DialogResult.OK)
                        {
                            GroupDataObject groupData = cg.Group;
                            clbGroups.Items[clbGroups.SelectedIndex] = groupData;
                        }
                    }
                }
                else if (contextMenuParent == ContextMenuParrent.cmpUsers && lbUsers.SelectedItem != null)
                {
                    UserInfoData userInfoData = (UserInfoData)((DataTable)(lbUsers.DataSource)).Rows[lbUsers.SelectedIndex][COLUMN_GENERIC];
                    List<GroupDataObject> groups = new List<GroupDataObject>();
                    usm.SolDev.Common.Config.ObjectState originalState = userInfoData.State;
                    userInfoData.State = usm.SolDev.Common.Config.ObjectState.Modified;

                    foreach (GroupDataObject groupDataObject in clbGroups.Items)
                    {
                        groups.Add(groupDataObject);
                    }

                    using (CreateUser cu = new CreateUser(userInfoData, groups))
                    {
                        if (cu.ShowDialog() == DialogResult.OK)
                        {
                            ((DataTable)(lbUsers.DataSource)).Rows[lbUsers.SelectedIndex][COLUMN_ID] = userInfoData.Id;
                            ((DataTable)(lbUsers.DataSource)).Rows[lbUsers.SelectedIndex][COLUMN_NAME] = userInfoData.Name;
                            ((DataTable)(lbUsers.DataSource)).Rows[lbUsers.SelectedIndex][COLUMN_GENERIC] = userInfoData;
                        }
                        else
                        {
                            userInfoData.State = originalState;
                        }
                    }
                }
             }
             #endregion
        }

        /// <summary>
        ///     Add/Remove a permission to the user
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void clbAccessRights_SelectedValueChanged(object sender, EventArgs e)
        {
            if (lbUsers.SelectedItem == null || clbAccessRights.SelectedIndex < 0)
            {
                return;
            }
            
            UserInfoData userInfo = (UserInfoData)((DataRowView)lbUsers.SelectedItem).Row[COLUMN_GENERIC];

            #region Add or remove access right
            // Keep already modified sate (in case if the user has been created)
            if (userInfo.State == usm.SolDev.Common.Config.ObjectState.Original)
            {
                userInfo.State = usm.SolDev.Common.Config.ObjectState.Modified;
            }
           
            Permissions userPermission = (Permissions )clbAccessRights.SelectedItem;

            if (clbAccessRights.GetItemChecked(clbAccessRights.SelectedIndex))
            {
                if (userInfo.HasPermission(((Permissions )clbAccessRights.SelectedItem).Id) == false)
                {
                    userInfo.UserPermitions.Add((Permissions)clbAccessRights.SelectedItem);
                }
            }
            else
            {
                if (userInfo.HasPermission(((Permissions)clbAccessRights.SelectedItem).Id))
                {
                    userInfo.UserPermitions.Remove((Permissions)clbAccessRights.SelectedItem);
                }
            }
            #endregion
        }

        /// <summary>
        ///     On save button click. Collect modified data and save changes (if any).
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void btnSave_Click(object sender, EventArgs e)
        {
            #region Defines of modified data lists
            List<UserInfoData> userInfoDataList = new List<UserInfoData>();
            List<GroupDataObject> groupInfoDataList = new List<GroupDataObject>();
            #endregion
            
            #region Detect if there are some changes in USER related data and save them
            foreach (DataRowView dataRowView in lbUsers.Items)
            {
                userInfoDataList.Add((UserInfoData)dataRowView[COLUMN_GENERIC]);
            }
            #endregion

            #region Detect if there are some changes in GROUP related data and save them
            foreach (GroupDataObject groupDataObject in clbGroups.Items)
            {
                groupInfoDataList.Add(groupDataObject);
            }
            #endregion

            #region If at least one items has it's state changed, save it ...
            if (userInfoDataList.Count > 0 || groupInfoDataList.Count > 0)
            {
                base.StartWaiting();
                (new SaveUserManagementDataDelegate(DataProvider.DataBase.SaveUserManagementData)).BeginInvoke(WorksessionData.UserInfo, userInfoDataList, groupInfoDataList, new AsyncCallback(SaveUserManagementDataCallBack), null);
            }
            #endregion

        }
        #endregion

        #region Get user management related data
        /// <summary>
        ///     Load user related data
        /// </summary>
        /// <param name="userInfoData"></param>
        /// <returns></returns>
        private delegate UserManagementData GetUserManagementDataDelegate(UserInfoData userInfoData);
        private void GetUserManagementDataCallBack(IAsyncResult ar)
        {
            try
            {
                UserManagementData userManagementData = ((GetUserManagementDataDelegate)((AsyncResult)ar).AsyncDelegate).EndInvoke(ar);
                if (!IsDisposed && IsHandleCreated)
                {
                    BeginInvoke(new GetUserManagementDataCompletedDelegate(GetUserManagementDataCompleted), new object[] { true, userManagementData });
                }
            }
            catch (Exception ex)
            {
                BeginInvoke(new GetUserManagementDataCompletedDelegate(GetUserManagementDataCompleted), new object[] { false, null });
                new SolDevException("Cannot get user managment data", ex);
            }
        }

        /// <summary>
        ///     After user related data has been loaded.
        /// </summary>
        /// <param name="success"></param>
        /// <param name="userManagementData"></param>
        private delegate void  GetUserManagementDataCompletedDelegate (bool success, UserManagementData userManagementData);
        private void GetUserManagementDataCompleted(bool success, UserManagementData userManagementData)
        {
            base.EndWaiting();
            if (success == true && userManagementData != null)
            {

                #region Fill groups
                clbGroups.Items.Clear();
                foreach(GroupDataObject groupData in userManagementData.Groups)
                {
                    clbGroups.Items.Add(groupData);
                }
                #endregion

                #region Fill permissions
                clbAccessRights.Items.Clear();
                foreach (Permissions permission in userManagementData.Permissions)
                {
                    clbAccessRights.Items.Add(permission);
                }
                #endregion

                #region populate users list
                DataTable dt = new DataTable();
                dt.Columns.Add(COLUMN_ID, typeof(int));
                dt.Columns.Add(COLUMN_NAME, typeof(string));
                dt.Columns.Add(COLUMN_GENERIC, typeof(object));
                foreach (UserInfoData userInfo in userManagementData.Users)
                {
                    dt.Rows.Add(new object[] { userInfo.Id, userInfo.Name, userInfo });
                }

                lbUsers.DataSource = dt;
                lbUsers.DisplayMember = COLUMN_NAME;
                #endregion
            }
        }
        #endregion

        #region Helper methods
        /// <summary>
        ///     Check if there is any modified object
        /// </summary>
        /// <returns>True if at least one object is modified</returns>
        private bool areObjectStatesModified()
        {
            foreach(DataRowView dataRowView in lbUsers.Items)
            {
                if (((UserInfoData)dataRowView[COLUMN_GENERIC]).State != usm.SolDev.Common.Config.ObjectState.Original)
                {
                    return true;
                }
            }

            foreach (GroupDataObject dataObjects in clbGroups.Items)
            {
                if (dataObjects.State != usm.SolDev.Common.Config.ObjectState.Original)
                {
                    return true;
                }
            }

            return false;
        }
        #endregion

        #region Save users and Groups management
        /// <summary>
        ///     Perform saving of user related data (trough delegates)
        /// </summary>
        /// <param name="userInfoData">Which user invokes this delegate</param>
        /// <param name="userInfoList">The list of modified users</param>
        /// <param name="groupInfoList">The list of modified groups</param>
        /// <returns>the operation result</returns>
        private delegate object [] SaveUserManagementDataDelegate(UserInfoData userInfoData, List<UserInfoData> userInfoList, List<GroupDataObject> groupInfoList);
        private void SaveUserManagementDataCallBack(IAsyncResult ar)
        {
            try
            {
                object [] resultObjects = ((SaveUserManagementDataDelegate)((AsyncResult)ar).AsyncDelegate).EndInvoke(ar);
                if (!IsDisposed && IsHandleCreated)
                {
                    if (resultObjects != null && resultObjects.Length == 2)
                    {
                        BeginInvoke(new SaveUserManagementDataCompletedDelegate(SaveUserManagementDataCompleted), new object[] { resultObjects[0], resultObjects[1] });
                    }
                    else
                    {
                        BeginInvoke(new SaveUserManagementDataCompletedDelegate(SaveUserManagementDataCompleted), new object[] { null, null });
                    }
                }
            }
            catch (Exception ex)
            {
                BeginInvoke(new SaveUserManagementDataCompletedDelegate(SaveUserManagementDataCompleted), null);
                new SolDevException("Cannot save changes", ex);
            }
        }

        /// <summary>
        ///     After the user data have been saved, update controls
        /// </summary>
        /// <param name="success">Set to true when operation has completed with no error</param>
        private delegate void SaveUserManagementDataCompletedDelegate(List<UserInfoData> usersDataList, List<GroupDataObject> groupDataList);
        private void SaveUserManagementDataCompleted(List<UserInfoData> usersDataList, List<GroupDataObject> groupDataList)
        {
            base.EndWaiting();

            #region Update users list
            if (usersDataList != null)
            {
                DataTable dt = new DataTable();
                dt.Columns.Add(COLUMN_ID, typeof(int));
                dt.Columns.Add(COLUMN_NAME, typeof(string));
                dt.Columns.Add(COLUMN_GENERIC, typeof(object));
                foreach (UserInfoData userInfo in usersDataList)
                {
                    dt.Rows.Add(new object[] { userInfo.Id, userInfo.Name, userInfo });
                }

                lbUsers.DataSource = dt;
                lbUsers.DisplayMember = COLUMN_NAME;
            }
            #endregion

            #region Update groups list
            if (groupDataList != null)
            {
                clbGroups.Items.Clear();
                foreach (GroupDataObject groupData in groupDataList)
                {
                    clbGroups.Items.Add(groupData);
                }
            }
            #endregion
        }
        #endregion
    }
}

